local lanes = require "lanes"

---! 获取所有玩家基本信息
local function _get_all_user_info(combat_ob)
    local players = combat_ob:get_player_obs()
    local resp = {}
    for _, player in ipairs(players) do
        table.insert(resp, USER_D:get_base_info(player))
    end
    return resp
end

---! 获取所有战斗数据
local function _get_all_combat_data(combat_ob)
    local result = {}
    for _, player in ipairs(combat_ob:get_player_obs()) do
        local tank = player:query_temp("tank")
        if tank then
            table.insert(result, tank:get_combat_data())
        end
    end
    return result
end

---! 获取战斗概述
local function _get_combat_detail_info(combat_ob)
    local details = {}
    for _, player in ipairs(combat_ob:get_player_obs()) do
        local tank = player:query_temp("tank")
        if tank then
            local detail = {}
            detail.user_id = player:get_id()
            detail.nick_name = player:get_nick_name()
            detail.tank_num = tank:get_tank_num()
            detail.tank_alive = tank:is_alive()
            table.insert(details, detail)
        end
    end

    local combat_details = combat_ob:query_temp("combat_detail")
    if combat_details then
        for _, detail in pairs(combat_details) do
            table.insert(details, detail)
        end
    end

    table.sort(
        details,
        function(a, b)
            local a_score = a.tank_alive * 1000 + a.tank_num
            local b_score = b.tank_alive * 1000 + b.tank_num
            return a_score > b_score
        end
    )
    return details
end

---! 战斗结算
local function _bonus_combat(combat_ob, user_ob)
    local tank = user_ob:query_temp("tank")
    if not tank then
        ---! 找不到玩家当前载具
        return
    end

    ---! 排名按照死亡时间计算
    local rank = combat_ob:get_user_num() - combat_ob:get_dead_num() + 1
    ---! 根据排名获取经验
    local exp = USER_D:get_exp_by_top(rank)
    ---! 给玩家增加经验
    USER_D:add_exp(user_ob, exp)

    spdlog.debug("combat", "======== finish combat settlement")
    spdlog.debug("combat", "==== user_num : %s", combat_ob:get_user_num())
    spdlog.debug("combat", "==== daed_num : %s", combat_ob:get_dead_num())
    spdlog.debug("combat", "==== tank_num : %s", tank:get_tank_num())
    spdlog.debug("combat", "==== alive : %s", math.max(0, tank:get_dead_time() - combat_ob:get_begin_time()))
    spdlog.debug("combat", "==== rank : %s", rank)
    spdlog.debug("combat", "==== exp : %d", exp)

    local result = {}
    result.tank_num = tank:get_tank_num()
    result.duration = math.max(0, tank:get_dead_time() - combat_ob:get_begin_time())
    result.exp = exp
    result.rank = rank
    user_ob:send_packet("tanker.S2CFinishCombat", result)
end

---! 战斗退出
local function _quit_combat(combat_ob, user_ob)
    if combat_ob ~= user_ob:get_combat() then
        return
    end

    ---! 优先退出战斗
    user_ob:quit_combat()

    ---! 再做消息广播
    local result = {}
    result.status = 0
    result.uid = user_ob:get_id()
    user_ob:send_packet("tanker.S2CQuitCombat", result)
    combat_ob:brocast_packet("tanker.S2CQuitCombat", result)

    ---! 记录玩家退出时间
    combat_ob:set_temp("quit_time", user_ob:get_id(), os.time())

    ---! 最后处理后续操作
    local players = combat_ob:get_player_obs(true)
    if #players <= 0 then
        combat_ob:clear_combat()
    end
end

---! 子模块映射
local entries = {}
register_post_init(function()
    load_all("daemon\\combat", true)
end)

-------------------------------------------------------------------------------
---! 对外接口
-------------------------------------------------------------------------------
COMBAT_D = {}

function COMBAT_D:register_entry(name, module)
    entries[name] = module
end

function COMBAT_D:query_entry(name)
    return entries[name]
end

---------------------------------------
---!
---------------------------------------

function COMBAT_D:C2SFinishLoading(user_ob, msg)
    local module = COMBAT_D:query_entry("loading")
    if not module then
        return
    end

    local schedule = msg.schedule
    return module:finish_loading(user_ob, schedule)
end

function COMBAT_D:C2SPlayInput(user_ob, msg)
    local module = COMBAT_D:query_entry("action")
    if not module then
        return
    end

    local frame_data = msg.frame_data
    if not frame_data then
        return
    end

    local combat_ob = user_ob:get_combat()
    if not combat_ob then
        return
    end

    return module:input_frame(combat_ob, user_ob, frame_data)
end

function COMBAT_D:C2SHit(user_ob, hit_user_id, hit_mode)
    local module = COMBAT_D:query_entry("action")
    if not module then
        return
    end

    local combat_ob = user_ob:get_combat()
    if not combat_ob then
        return
    end

    if hit_mode ~= 0 then
        module:crash_frame(combat_ob, user_ob, hit_mode)
    end

    return module:hitted_frame(combat_ob, user_ob, hit_user_id)
end

function COMBAT_D:C2SGetCombatDetailInfo(user_ob)
    local combat_ob = user_ob:get_combat()
    if not combat_ob then
        return
    end

    local result = {}
    result.details = _get_combat_detail_info(combat_ob)
    user_ob:send_packet("tanker.S2CGetCombatDetailInfo", result)
end

function COMBAT_D:C2SQuitCombat(user_ob)
    local combat_ob = user_ob:get_combat()
    if not combat_ob then
        return
    end

    if combat_ob:query_temp("quit_time", user_ob:get_id()) then
        return
    end

    local tank = user_ob:query_temp("tank")
    if tank and not tank:is_dead() then
        ---! 做一次自杀结算
        COMBAT_D:C2SHit(user_ob, user_ob:get_id(), 2)
    end

    ---! 立即退出战斗
    _quit_combat(combat_ob, user_ob)
end

---------------------------------------
---!
---------------------------------------

---! 加载战斗 从匹配队列数据移到战斗中
function COMBAT_D:loading_combat(match_room)
    local module = COMBAT_D:query_entry("loading")
    if not module then
        return
    end
    return module:start_loading(match_room:get_user_obs())
end

---! 准备战斗
function COMBAT_D:prepare_combat(combat_ob)
    ---! 战斗已开始
    if combat_ob:get_fighting() then
        return
    end

    ---! 标记战斗已经开始
    combat_ob:set_fighting(true)

    ---! 取消定时器
    local timer_id = combat_ob:query_temp("prepare_timer_id")
    if timer_id then
        TIMER_D:cancel_timer(timer_id)
        combat_ob:delete_temp("prepare_timer_id")
    end

    local players = combat_ob:get_player_obs(true)
    if table.size(players) == 0 then
        ---! 清理战斗
        combat_ob:clear_combat()
        return
    end

    local module = COMBAT_D:query_entry("prepare")
    if not module then
        ---! 初始模块不存在
        return false
    end

    if not module:prepare_combat(combat_ob) then
        return false
    end

    ---! 进入游戏场景后，玩家此时不能移动，攻击，需要倒计时3，2，1，开始
    -- 因此，游戏需要延迟4秒正式开启
    TIMER_D:start_timer(4, 1, function()
        COMBAT_D:start_combat(combat_ob)
    end)

    ---! 记录开始时间
    local start_time = combat_ob:set_begin_time((lanes.now_secs() + 4)* 1000)

    ---! 广播消息
    local result = {}
    result.start_time = start_time
    result.users = _get_all_user_info(combat_ob)
    result.combat_data = _get_all_combat_data(combat_ob)
    combat_ob:brocast_packet("tanker.S2CStartCombat", result)
    return true
end

---! 开始战斗
function COMBAT_D:start_combat(combat_ob)
    ---! 启动同步帧计时器
    local module = COMBAT_D:query_entry("action")
    if module then
        module:start_sync_timer(combat_ob)
    end
end

---! 结束战斗
function COMBAT_D:finish_combat(combat_ob)
    local tanks = {}
    for _, player in ipairs(combat_ob:get_player_obs()) do
        local tank = player:query_temp("tank")
        if tank and not tank:is_dead() then --结束战斗时只发给胜利的玩家,其它玩家在死亡时已发送
            table.insert(tanks, tank)
        end
    end
    ---! 调整为按照死亡时间计算
    --[[
    table.sort(tanks, function(a, b)
        local a_score = a:get_dead_time() * 1000 -- + a:get_tank_num()
        local b_score = b:get_dead_time() * 1000 -- + b:get_tank_num()
        return a_score > b_score
    end)
    --]]

    local now = lanes.now_secs() * 1000
    for rank = 1, #tanks do
        local tank = tanks[rank]
        local user_ob = USER_D:find_user(tank:get_owner_id())
        if user_ob then
            ---! 根据排名获取经验
            local exp = USER_D:get_exp_by_top(rank)

            ---! 给玩家增加经验
            USER_D:add_exp(user_ob,exp)

            spdlog.debug("combat", "======== finish combat settlement")
            spdlog.debug("combat", "==== user_num : %s", combat_ob:get_user_num())
            spdlog.debug("combat", "==== daed_num : %s", combat_ob:get_dead_num())
            spdlog.debug("combat", "==== tank_num : %s", tank:get_tank_num())
            spdlog.debug("combat", "==== alive : %s", math.max(0, now - combat_ob:get_begin_time()))
            spdlog.debug("combat", "==== rank : %s", rank)
            spdlog.debug("combat", "==== exp : %d", exp)

            ---! 发送战斗结算信息
            local result = {}
            result.tank_num = tank:get_tank_num()
            result.duration = math.max(0, now - combat_ob:get_begin_time())
            result.exp = exp
            result.rank = rank
            user_ob:send_packet("tanker.S2CFinishCombat", result)

            ---! 通知玩家退出战斗
            local result = {}
            result.status = 1
            result.uid = user_ob:get_id()
            user_ob:send_packet("tanker.S2CQuitCombat", result)

            ---! 退出战斗
            user_ob:quit_combat()

            ---! 记录玩家退出时间
            combat_ob:set_temp("quit_time", user_ob:get_id(), os.time())

            ---! 队伍解散
            TEAM_D:dismiss_team(TEAM_D:get_team(user_ob))
        end
    end

    ---! 延缓一分钟清理战斗
    TIMER_D:start_timer(60, 1, function()
        combat_ob:clear_combat()
    end)
end

---! 退出战斗
function COMBAT_D:quit_combat(user_ob, delay)
    if user_ob:is_robot() then
        ---! 若是机器人无需处理
        return
    end

    local combat_ob = user_ob:get_combat()
    if not combat_ob then
        ---! 当前玩家不在战斗中
        return
    end

    ---! 战斗结算
    _bonus_combat(combat_ob, user_ob)

    if user_ob:is_robot() then
        ---! 当前若是机器人，立即执行 退出流程
        _quit_combat(combat_ob, user_ob)
        return
    end

    if not delay then
        ---! 非延迟调用，立即执行 退出流程
        _quit_combat(combat_ob, user_ob)
    else
        ---! S2CFinishCombat 消息发送成功后，需要客户端发送 C2SQuitCombat 确认退出流程
        ---! 但为避免客户端迟迟未收到 C2SQuitCombat 需做容错处理，暂定30秒主动触发COMBAT_D:quit_combat
        TIMER_D:start_timer(delay, 1, function()
            _quit_combat(combat_ob, user_ob)
        end)
    end
end

---! 记录死亡信息
function COMBAT_D:set_dead_time(combat_ob, user_ob, tank)
    ---! 记录目坦克死亡时间
    tank:set_dead_time(lanes.now_secs() * 1000)

    ---! 备份当前玩家最后的战斗信息
    local detail = {}
    detail.user_id = user_ob:get_id()
    detail.nick_name = user_ob:get_nick_name()
    detail.tank_num = tank:get_tank_num()
    detail.tank_alive = tank:is_alive()
    combat_ob:set_temp("combat_detail", user_ob:get_id(), detail)
end
